package system

import (
	consts2 "MoSkeleton/config/consts"
	"MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/core/dto/request"
	"MoSkeleton/framework/core/dto/response"
	"MoSkeleton/framework/core/models"
	"MoSkeleton/framework/core/moerrors"
	"MoSkeleton/framework/mo_gorm"
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/system"
	"MoSkeleton/services/base"
	s1 "MoSkeleton/web/pogos/request/system"
	system2 "MoSkeleton/web/pogos/response/system"
	"bytes"
	"errors"
	mapset "github.com/deckarep/golang-set/v2"
	"github.com/gin-gonic/gin"
	"github.com/samber/lo"
	"gorm.io/gorm"
	"strconv"
	"strings"
)

var selectedFields = []string{"r.id", "r.role_name", "r.role_key", "r.role_sort",
	"r.data_scope", "r.menu_check_strictly", "r.dept_check_strictly", "r.status", "r.del_flag",
	"r.created_at", "r.remark"}

type SysRoleService struct {
	base.BaseService
}

/*
		    select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
	            r.status, r.del_flag, r.create_time, r.remark
	        from sys_role r
		        left join sys_user_role ur on ur.role_id = r.role_id
		        left join sys_user u on u.user_id = ur.user_id
		        left join sys_dept d on u.dept_id = d.dept_id

WHERE r.del_flag = '0' and ur.user_id = #{userId}
*/
func (s *SysRoleService) GetRoleKeysByUserId(userId uint64) (*mapset.Set[string], error) {
	var roles []system.SysRole
	db := s.getRoleVo(nil).Distinct(selectedFields)

	if err := db.Where("r.del_flag = ? and ur.user_id = ?", "0", userId).Find(&roles).Error; err != nil {
		return nil, err
	}
	if len(roles) == 0 {
		set := mapset.NewSet[string]()
		return &set, nil
	}
	roleKeys := mapset.NewSet[string]()
	for _, role := range roles {
		roleKeys.Add(role.RoleKey)
	}
	return &roleKeys, nil
}

func (s *SysRoleService) GetRolesByUserId(c *gin.Context, userId uint64) ([]*system2.SysRoleItemResp, error) {
	itemsResp, err := s.getRolePermissionByUserid(nil, userId)
	if err != nil {
		return nil, err
	}
	idMap := lo.Associate(itemsResp, func(item *system2.SysRoleItemResp) (uint64, int) {
		return item.ID, 1
	})
	roleReq := new(s1.SysRolePageReq)
	roleReq.PageInfo = request.PageInfo{Page: 0, PageSize: 1000}
	pageResult, err := s.GetRolePage(c, roleReq)
	if err != nil {
		return nil, err
	}
	itemsResult := pageResult.Rows.(*[]*system2.SysRoleItemResp)
	var itTrue bool = true

	lo.ForEach(*itemsResult, func(item *system2.SysRoleItemResp, index int) {
		if _, ok := idMap[item.ID]; ok {
			item.Flag = &itTrue
		}
	})
	return *itemsResult, nil
}

/*
select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,

	            r.status, r.del_flag, r.create_time, r.remark
	        from sys_role r
		        left join sys_user_role ur on ur.role_id = r.role_id
		        left join sys_user u on u.user_id = ur.user_id
		        left join sys_dept d on u.dept_id = d.dept_id

where r.del_flag = '0'

	<if test="roleId != null and roleId != 0">
		AND r.role_id = #{roleId}
	</if>
	<if test="roleName != null and roleName != ''">
		AND r.role_name like concat('%', #{roleName}, '%')
	</if>
	<if test="status != null and status != ''">
		AND r.status = #{status}
	</if>
	<if test="roleKey != null and roleKey != ''">
		AND r.role_key like concat('%', #{roleKey}, '%')
	</if>
	<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
		and date_format(r.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
	</if>
	<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
		and date_format(r.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
	</if>
	<!-- 数据范围过滤 -->
	${params.dataScope}
	order by r.role_sort
*/
func (s *SysRoleService) GetRolePage(c *gin.Context, roleReq *s1.SysRolePageReq) (*response.PageResult, error) {
	db := s.getPageReq(nil, roleReq)
	result := make([]*system2.SysRoleItemResp, 0)
	s.QueryDataScope(c, db)
	s.SetDataScopeDeptAlias(db, "d.")

	defer func() {
		mo_gorm.ResetDataScopeCount(db)
	}()
	var total int64
	db.Select("COUNT(DISTINCT " + strings.Join(selectedFields, ",") + ")").Count(&total)
	db.Select("DISTINCT " + strings.Join(selectedFields, ","))
	if err := db.Offset(roleReq.Offset()).Limit(roleReq.PageSize).Find(&result).Error; err != nil {
		return nil, err
	}
	pageResult := response.NewFromPageInfoAndData(&roleReq.PageInfo, &result, total)
	return pageResult, nil
}

func (s *SysRoleService) CountByDataScope(c *gin.Context, ids ...uint64) (int64, error) {
	roleReq := new(s1.SysRolePageReq)
	db := s.getPageReq(nil, roleReq).Select("COUNT(DISTINCT " + strings.Join(selectedFields, ",") + ")")
	db.Where("r.id in (?)", ids)
	s.QueryDataScope(c, db)
	s.SetDataScopeDeptAlias(db, "d.")

	defer func() {
		mo_gorm.ResetDataScopeCount(db)
	}()
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return 0, err
	}
	return total, nil
}

func (s *SysRoleService) GetRolesByUserDetailId(userId *uint64) ([]*system2.SysUserDetailRoleItemResp, error) {
	selectFields := "r.id , r.role_name, r.role_key, r.role_sort, r.data_scope, r.status"
	db := base.BaseDb.Select(selectFields).Table("sys_role r")
	if userId != nil {
		db.Joins("left join sys_user_role ur on r.id = ur.role_id")
		db.Where("ur.user_id = ?", userId)
	}
	db.Where(" del_flag=?", "0")
	itemsResp := make([]*system2.SysUserDetailRoleItemResp, 0)
	result := db.Find(&itemsResp)
	return itemsResp, result.Error
}

func (s *SysRoleService) AddRole(addReq *s1.SysRoleAddReq) (bool, error) {
	role := &system.SysRole{}
	if unique, err := role.CheckUniqueName(nil, addReq.RoleName); err != nil {
		return false, err
	} else if !unique {
		return false, moerrors.NewValidateError(consts.CommCreatFailCode, "角色名称已存在")
	}
	if unique, err := role.CheckUniqueKey(nil, addReq.RoleKey); err != nil {
		return false, err
	} else if !unique {
		return false, moerrors.NewValidateError(consts.CommCreatFailCode, "角色权限已存在")
	}

	if err := utils.CopyStruct(role, addReq); err != nil {
		return false, moerrors.NewValidateError(consts.CommCreatFailCode, "参数转换失败")
	}

	role.DelFlag = consts2.U_NORMAL
	var affectedCount int64
	err := base.BaseDb.Transaction(func(tx *gorm.DB) error {
		result := s.GetDb(tx).Create(role)
		if result.Error != nil {
			return result.Error
		}
		affectedCount = result.RowsAffected
		if addReq.MenuIds != nil {
			menuIds := lo.Map(*addReq.MenuIds, func(menuId string, _ int) uint64 {
				mId, _ := strconv.ParseUint(menuId, 10, 64)
				return mId
			})
			if _, err := s.insertRoleMenus(tx, role.ID, &menuIds); err != nil {
				return err
			}
		}
		return nil
	})

	return affectedCount > 0, err
}

func (s *SysRoleService) UpdateRoleById(c *gin.Context, updateReq *s1.SysRoleUpdateReq) (bool, error) {
	role := &system.SysRole{}
	if err := utils.CopyStruct(role, updateReq); err != nil {
		return false, err
	}
	if role.IsAdmin() {
		return false, moerrors.NewValidateError(consts.CommUpdateFailCode, "不能修改管理员角色")
	}
	//DONE datascope
	if err := s.CheckRoleDataScope(c, updateReq.ID); err != nil {
		return false, err
	}
	if unique, err := role.CheckUniqueName(nil, role.RoleName); err != nil {
		return false, err
	} else if !unique {
		return false, moerrors.NewValidateError(consts.CommUpdateFailCode, "角色名称已存在")
	}
	if unique, err := role.CheckUniqueKey(nil, role.RoleKey); err != nil {
		return false, err
	} else if !unique {
		return false, moerrors.NewValidateError(consts.CommUpdateFailCode, "角色权限已存在")
	}
	err := base.BaseDb.Transaction(func(tx *gorm.DB) error {
		if count, err := role.UpdateRoleById(tx); err != nil {
			return err
		} else {
			if count > 0 {
				// 删除角色与菜单关联
				_, err := new(system.SysRoleMenu).DeleteByRoleIds(tx, &[]uint64{updateReq.ID})
				if err != nil {
					return err
				}
				menuIds := lo.Map(*updateReq.MenuIds, func(menuId string, _ int) uint64 {
					mId, _ := strconv.ParseUint(menuId, 10, 64)
					return mId
				})
				if _, err := s.insertRoleMenus(tx, updateReq.ID, &menuIds); err != nil {
					return err
				}
			}
		}
		return nil
	})
	return err == nil, err

}

func (s *SysRoleService) UpdateRoleStatus(c *gin.Context, req *s1.ChangeRoleStatusReq) (bool, error) {
	sysRole := &system.SysRole{
		MoModel: models.MoModel{ID: req.ID},
	}
	if sysRole.IsAdmin() {
		return false, moerrors.NewValidateError(consts.CommUpdateFailCode, "不能修改管理员角色")
	}
	//DONE Data Scope
	if err := s.CheckRoleDataScope(c, req.ID); err != nil {
		return false, err
	}
	return sysRole.UpdateStatus(nil, req.Status)
}

func (s *SysRoleService) UpdateDataScope(c *gin.Context, updateReq *s1.SysRoleUpdateReq) (bool, error) {
	sysRole := &system.SysRole{
		MoModel: models.MoModel{ID: updateReq.ID},
	}
	if sysRole.IsAdmin() {
		return false, moerrors.NewValidateError(consts.CommUpdateFailCode, "不能修改管理员角色")
	}

	//DONE Data Scope
	if err := s.CheckRoleDataScope(c, updateReq.ID); err != nil {
		return false, err
	}
	return s.authDataScope(sysRole, updateReq)
}

func (s *SysRoleService) DeleteRoleByIds(c *gin.Context, ids *[]uint64) (bool, error) {
	role := &system.SysRole{}
	if role.IdsHasAdmin(ids) {
		return false, moerrors.NewValidateError(consts.CommDeleteFailCode, "不能删除管理员角色")
	}
	if err := s.CheckRoleDataScope(c, *ids...); err != nil {
		return false, err
	}
	if relateRole, err := s.getFirstRoleByRelateUser(ids); err != nil {
		return false, err
	} else if relateRole != nil {
		return false, moerrors.NewValidateError(consts.CommDeleteFailCode, "角色:"+relateRole.RoleName+",下存在用户，不能删除")
	}
	//return roleMapper.deleteRoleByIds(roleIds);
	err := base.BaseDb.Transaction(func(tx *gorm.DB) error {
		// 删除角色与菜单关联
		if _, err := new(system.SysRoleMenu).DeleteByRoleIds(tx, ids); err != nil {
			return err
		}
		// 删除角色与部门关联
		if _, err := new(system.SysRoleDept).DeleteByRoleIds(tx, ids); err != nil {
			return err
		}
		if _, err := role.DeleteByRoleIds(tx, ids); err != nil {
			return err
		}
		return nil
	})
	return err == nil, err
}

func (s *SysRoleService) GetRoleById(c *gin.Context, id uint64) (*system2.SysRoleItemResp, error) {
	if err := s.CheckRoleDataScope(c, id); err != nil {
		return nil, err
	}
	var role system.SysRole
	sysRole, err := role.GetById(nil, id)
	if err != nil {
		return nil, err
	}
	roleItem := system2.SysRoleItemResp{}

	_ = utils.CopyStruct(&roleItem, sysRole)
	return &roleItem, nil
}
func (s *SysRoleService) GetRoleNamesByUserId(userId uint64) ([]string, error) {
	db := s.getRoleVo(nil).Distinct(selectedFields)
	db.Where("ur.user_id = ? and r.del_flag= ? ", userId, 0)
	itemsResp := make([]*system2.SysRoleItemResp, 0)
	result := db.Find(&itemsResp)
	if result.Error != nil {
		return nil, result.Error
	}
	return lo.Map(itemsResp, func(item *system2.SysRoleItemResp, _ int) string {
		return item.RoleName
	}), nil
}

func (s *SysRoleService) CheckRoleDataScope(c *gin.Context, ids ...uint64) error {
	userId := utils.GetUserIdFromGin(c)
	if system.IsUserAdmin(userId) {
		return nil
	}
	if count, err := s.CountByDataScope(c, ids...); err != nil {
		return err
	} else if count == 0 {
		return moerrors.NewValidateError(consts.DataScopeLimitedCode, "没有权限访问角色数据！")
	}
	return nil
}

func (s *SysRoleService) authDataScope(sysRole *system.SysRole, updateReq *s1.SysRoleUpdateReq) (bool, error) {

	if err := utils.CopyStruct(sysRole, updateReq); err != nil {
		return false, err
	}
	err := base.BaseDb.Transaction(func(tx *gorm.DB) error {
		if _, err := sysRole.UpdateRoleById(tx); err != nil {
			return err
		}
		if _, err := sysRoleDeptService.DeleteRoleDeptByRoleId(tx, sysRole.ID); err != nil {
			return err
		}
		deptIds := lo.Map(*updateReq.DeptIds, func(deptId string, _ int) uint64 {
			dId, _ := strconv.ParseUint(deptId, 10, 64)
			return dId
		})
		if _, err := sysRoleDeptService.AddRoleDepts(tx, sysRole.ID, deptIds); err != nil {
			return err
		}
		return nil
	})
	return err == nil, err
}

/*
select sr.* from sys_role sr
join sys_user_role sur on sr.id =sur.role_id
where sr.id in (3)
*/
func (s *SysRoleService) getFirstRoleByRelateUser(roleIds *[]uint64) (*system.SysRole, error) {
	var role system.SysRole
	db := base.BaseDb.Table("sys_role sr").Select("sr.*")
	db.Joins("join sys_user_role sur on sr.id =sur.role_id")
	db.Where("sr.id in (?)", *roleIds)
	err := db.First(&role).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	return &role, err
}

func (s *SysRoleService) GetDeptTreeByRole(c *gin.Context, id uint64) (map[string]interface{}, error) {
	deptIds, err := sysDeptService.GetDeptIdListByRoleId(id)
	if err != nil {
		return nil, err
	}
	result := make(map[string]interface{})
	deptIdStrs := lo.Map(*deptIds, func(deptId uint64, _ int) string {
		return strconv.FormatUint(deptId, 10)
	})
	result["checkedKeys"] = deptIdStrs
	deptReq := &s1.SysDeptPageReq{}
	deptTreeSelects, err := sysDeptService.GetDeptTreeList(c, deptReq)
	if err != nil {
		return nil, err
	}
	result["depts"] = deptTreeSelects
	return result, nil
}

func (s *SysRoleService) ExportRole(roleReq *s1.SysRolePageReq) (*bytes.Buffer, error) {
	db := s.getPageReq(nil, roleReq).Select("DISTINCT " + strings.Join(selectedFields, ","))
	roleItemsResp := make([]*system2.SysRoleItemResp, 0)
	result := db.Find(&roleItemsResp)
	if result.Error != nil {
		return nil, result.Error
	}
	titles := []string{"角色编号", "角色名称", "角色权限", "显示顺序", "数据范围", "角色状态"}
	exportFile := utils.NewExportExcelFile()

	return exportFile.Export(consts2.Excel_Sheet_Role, &titles, func(row, col int, colTitle string) (value any, ok bool, err error) {
		if len(roleItemsResp) <= row {
			return "", false, nil
		}
		itemResp := roleItemsResp[row]
		switch col {
		case 0:
			return "'" + strconv.FormatUint(itemResp.ID, 10), true, nil
		case 1:
			return itemResp.RoleName, true, nil
		case 2:
			return itemResp.RoleKey, true, nil
		case 3:
			return itemResp.RoleSort, true, nil
		case 4:
			//1：所有数据权限；2：自定义数据权限；3：本部门数据权限；4：本部门及以下数据权限；5：仅本人数据权限
			if itemResp.DataScope == consts2.Data_Scope_All {
				return "所有数据权限", true, nil
			} else if itemResp.DataScope == consts2.Data_Scope_Custom {
				return "自定义数据权限", true, nil
			} else if itemResp.DataScope == consts2.Data_Scope_Dept {
				return "本部门数据权限", true, nil
			} else if itemResp.DataScope == consts2.Data_Scope_Dept_And_Child {
				return "本部门及以下数据权限", true, nil
			} else if itemResp.DataScope == consts2.Data_Scope_Self {
				return "仅本人数据权限", true, nil
			}
		case 5:
			if itemResp.Status == consts2.U_NORMAL {
				return "正常", true, nil
			} else {
				return "停用", true, nil
			}
		}
		return "", false, nil
	})
}

func (s *SysRoleService) insertRoleMenus(tx *gorm.DB, roleId uint64, menuIds *[]uint64) (int64, error) {
	roleMenus := make([]*system.SysRoleMenu, 0)
	for _, menuId := range *menuIds {
		roleMenus = append(roleMenus, &system.SysRoleMenu{
			MenuId: menuId,
			RoleId: roleId,
		})
	}
	return new(system.SysRoleMenu).AddBatch(tx, roleMenus)
}

func (s *SysRoleService) getRoleVo(tx *gorm.DB) *gorm.DB {
	db := s.GetDb(tx).Table("sys_role r")
	db.Joins("left join sys_user_role ur on ur.role_id = r.id")
	db.Joins("left join sys_user u on u.id = ur.user_id")
	db.Joins("left join sys_dept d on u.dept_id = d.id").Where("r.del_flag = ?", "0")
	return db
}

func (s *SysRoleService) getPageReq(tx *gorm.DB, roleReq *s1.SysRolePageReq) *gorm.DB {
	db := s.getRoleVo(tx)
	if roleReq.ID != 0 {
		db.Where("r.id = ?", roleReq.ID)
	}
	if roleReq.RoleName != "" {
		db.Where("r.role_name like ?", "%"+roleReq.RoleName+"%")
	}
	if roleReq.Status != "" {
		db.Where("r.status = ?", roleReq.Status)
	}
	if roleReq.RoleKey != "" {
		db.Where("r.role_key like ?", "%"+roleReq.RoleKey+"%")
	}
	beginTime, ok := roleReq.GetParamValue("beginTime")
	if ok && beginTime != "" {
		if beginTime = roleReq.FormatBeginTime(beginTime); beginTime != "" {
			db.Where("r.created_at >= ?", beginTime)
		}
	}
	endTime, ok := roleReq.GetParamValue("endTime")
	if ok && endTime != "" {
		if endTime = roleReq.FormatEndTime(endTime); endTime != "" {
			db.Where("r.created_at <= ?", endTime)
		}
	}
	db.Order("r.role_sort")
	return db
}

func (s *SysRoleService) getRolePermissionByUserid(tx *gorm.DB, userId uint64) ([]*system2.SysRoleItemResp, error) {
	itemsResp := make([]*system2.SysRoleItemResp, 0)
	db := s.getRoleVo(tx).Distinct(selectedFields)
	result := db.Where("ur.user_id=?", userId).Find(&itemsResp)
	return itemsResp, result.Error
}
